package com.open.basic.json;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;


public class JsonClientImpl implements JsonClient {

    private static final Logger log = LoggerFactory.getLogger(JsonClientImpl.class);
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private JsonMapper jsonMapper;

    public void init() {
        // 这个特性，决定了解析器是否将自动关闭那些不属于parser自己的输入源。
        // 如果禁止，则调用应用不得不分别去关闭那些被用来创建parser的基础输入流InputStream和reader；
        // 默认是true
        jsonMapper = JsonMapper.builder()
                .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true)
                .configure(JsonParser.Feature.ALLOW_COMMENTS, true)
                .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
                .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, false)
                .configure(SerializationFeature.WRAP_ROOT_VALUE, false)
                .configure(SerializationFeature.INDENT_OUTPUT, false)
                .configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true)
                .configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, false)
                .configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true)
                .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, false)
                .configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .visibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
                .visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
                .build();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);

        LocalDateTimeDeserializer dateTimeDeserializer = new LocalDateTimeDeserializer(formatter);
        LocalDateTimeSerializer dateTimeSerializer = new LocalDateTimeSerializer(formatter);

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, dateTimeDeserializer);
        javaTimeModule.addSerializer(LocalDateTime.class, dateTimeSerializer);
        jsonMapper.registerModule(javaTimeModule);

        jsonMapper.setTimeZone(TimeZone.getDefault());
        jsonMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
        jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    }

    /**
     * 将对象转换json形式
     *
     * @param obj 输入的对象
     * @return json串
     */
    @Override
    public String toJson(Object obj) {
        String json;
        try {
            if (log.isDebugEnabled()) {
                log.debug(" begin transform into json from obj:{}", obj);
            }
            json = jsonMapper.writeValueAsString(obj);
            if (log.isDebugEnabled()) {
                log.debug(" end transform into json:{}", json);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException("json processing error!", e);
        }
        return json;

    }

    /**
     * json字符串转换为对象
     *
     * @param json  json字符串
     * @param clazz 要转换的简单对象类型
     * @param <T>   对象类型
     * @return 对象
     */
    @Override
    public <T> T fromJson(String json, Class<T> clazz) {
        T t;
        try {
            if (log.isDebugEnabled()) {
                log.debug(" begin transform [{}] into class:{}", json, clazz);
            }
            t = jsonMapper.readValue(json, clazz);
            if (log.isDebugEnabled()) {
                log.debug(" end transform [{}] into class:{}", json, clazz);
            }
        } catch (IOException e) {
            throw new RuntimeException("de json error", e);
        }

        return t;

    }

    /**
     * 转换为对象列表
     *
     * @param json  json 字符串
     * @param clazz 对象列项
     * @param <T>   对象类型
     * @return 对象列表
     */
    @Override
    public <T> List<T> toList(String json, Class<T> clazz) {
        List<T> t;
        try {
            if (log.isDebugEnabled()) {
                log.debug(" begin transform [{}] into class:{}", json, clazz);
            }
            t = jsonMapper.readValue(json, TypeFactory.defaultInstance()
                    .constructCollectionType(List.class, clazz));
            if (log.isDebugEnabled()) {
                log.debug(" end transform [{}] into class:{}", json, clazz);
            }
        } catch (IOException e) {
            throw new RuntimeException("de json error", e);
        }
        return t;
    }

    /**
     * json转换为Map对象
     *
     * @param json       json字符串
     * @param keyClazz   key对象类型
     * @param valueClazz 值对象类型
     * @param <K>        key值类型
     * @param <V>        值类型
     * @return map对象
     */
    @Override
    public <K, V> Map<K, V> toMap(String json, Class<K> keyClazz, Class<V> valueClazz) {
        Map<K, V> t;
        try {
            if (log.isDebugEnabled()) {
                log.debug(" begin transform [{}] into class:{},{}", json, keyClazz, valueClazz);
            }
            t = jsonMapper.readValue(json, TypeFactory.defaultInstance()
                    .constructMapLikeType(Map.class, keyClazz, valueClazz));
            if (log.isDebugEnabled()) {
                log.debug(" end transform [{}] into class:{},{}", json, keyClazz, valueClazz);
            }
        } catch (IOException e) {
            throw new RuntimeException("de json error", e);
        }
        return t;
    }

    /**
     * json转换为JsonNode对象 jsonNode类似JSONObject
     *
     * @param json
     * @return
     * @throws JsonProcessingException
     */
    public JsonNode toJsonNode(String json) throws JsonProcessingException {
        return jsonMapper.readTree(json);
    }

    public ObjectNode toObjectNode(String json) throws JsonProcessingException {
        return (ObjectNode) jsonMapper.readTree(json);
    }

    public ObjectNode toObjectNode(JsonNode jsonNode) {
        Map map = jsonMapper.convertValue(jsonNode, Map.class);
        return jsonMapper.valueToTree(map);
    }

    public ObjectNode toObjectNode(Map map) {
        return jsonMapper.valueToTree(map);
    }

    /**
     * 创建JsonNode对象
     *
     * @return
     */
    public ObjectNode createJsonNode() {
        return jsonMapper.createObjectNode();
    }
}
